package com.editso.haier.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class Promise<T, R> {
    Logger logger = LoggerFactory.getLogger(Promise.class);
    private boolean status = true;
    private T data = null;
    private final VoidFunctional<Callback<R, T>> functional;
    private Functional<R, T> aAccept;
    private Functional<R, T> eError;

    private final Queue<VoidFunctional<ItemCallback<R, T>>> accepts;
    private final Queue<VoidFunctional<ItemCallback<R, T>>> errors;

    private final ItemCallback<R, T> callback = new ItemCallback<R, T>() {

        @Override
        public T get() {
            return data;
        }

        @Override
        public void resolve(T o) {
            data = o;
            status = true;
        }

        @Override
        public void reject(T o) {
            data = o;
            status = false;
        }

        @Override
        public void and(boolean b, T t) {
            if (b) resolve(t);
            else reject(t);
        }

        @Override
        public void and(boolean b) {
            and(b, get());
        }

        @Override
        public void not(boolean n, T t) {
            and(!n, t);
        }

        @Override
        public void not(boolean b) {
            and(!b);
        }

        @Override
        public void accept(Functional<R, T> accept) {
            if (!status) return;
            aAccept = accept;
        }

        @Override
        public void error(Functional<R, T> error) {
            if (status) return;
            eError = error;
        }

    };

    public interface Callback<R, T> {
        void resolve(T o);

        void reject(T o);

        void and(boolean b, T t);

        void and(boolean b);

        void not(boolean n, T t);

        void not(boolean b);

        void accept(Functional<R, T> accept);

        void error(Functional<R, T> error);
    }

    public interface ItemCallback<R, T> extends Callback<R, T> {
        T get();
    }

    public interface VoidFunctional<T> {
        void apply(T t) throws Exception;
    }

    public interface Functional<R, T> {
        R apply(T t);
    }

    public Promise(VoidFunctional<Callback<R, T>> functional, Functional<R, T> accept, Functional<R, T> error) {
        this.functional = functional;
        this.aAccept = accept;
        this.eError = error;
        this.accepts = new ArrayDeque<>();
        this.errors = new ArrayDeque<>();
    }

    public Promise<T, R> accept(VoidFunctional<ItemCallback<R, T>> functional) {
        if (functional != null)
            accepts.add(functional);
        return this;
    }

    public Promise<T, R> error(VoidFunctional<ItemCallback<R, T>> functional) {
        if (functional != null)
            errors.add(functional);
        return this;
    }

    private void applyAccept() {
        if (accepts.isEmpty())
            return;
        try {
            VoidFunctional<ItemCallback<R, T>> item = accepts.remove();
            item.apply(callback);
            if (status) applyAccept();
            else applyError();
        } catch (Exception e) {
            logger.error("acceptError", e);
            applyError();
        }
    }

    private void applyError() {
        if (errors.isEmpty()) {
            status = false;
            return;
        }
        try {
            VoidFunctional<ItemCallback<R, T>> item = errors.remove();
            item.apply(callback);
            applyError();
        } catch (Exception e) {
            logger.error("runError", e);
            applyError();
        }
    }

    public R get() {
        try {
            functional.apply(callback);
            applyAccept();
        } catch (Exception e) {
            logger.error("err", e);
            applyError();
        }
        return status ? aAccept.apply(data) : eError.apply(data);
    }

    public R opt(R r) {
        R r1 = get();
        return r1 == null ? r : r1;
    }
}
